package com.mallcai.bigdata.ladon.service.export.impl;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.mallcai.bigdata.ladon.client.export.ExportResultVO;
import com.mallcai.bigdata.ladon.client.export.ExportStrategy;
import com.mallcai.bigdata.ladon.client.export.oridata.DirectConnectDbOriData;
import com.mallcai.bigdata.ladon.client.vo.ReportDataVO;
import com.mallcai.bigdata.ladon.dpl.entity.export.ExportTask;
import com.mallcai.bigdata.ladon.service.export.ITaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.OrderComparator;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;

/**
 * described :
 *
 * @author CaKeDou
 * @date 2019-09-25 17:43
 * Modify:
 */
@Slf4j
@Component
public class ExportPipeline implements ApplicationContextAware {

    private ApplicationContext applicationContext;
    @Autowired
    private ITaskService       taskService;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private List<IExportHandler> exportHandlerList = new ArrayList<>();

    public List<IExportHandler> getExportHandlerList() {
        return exportHandlerList;
    }

    @PostConstruct
    private void init() {
        this.applicationContext.getBeansOfType(IExportHandler.class)
                .values().stream()
                .sorted(OrderComparator.INSTANCE)
                .forEach(this.exportHandlerList::add);
    }

    private ScheduledExecutorService threadPool = new ScheduledThreadPoolExecutor(3,
            new BasicThreadFactory.Builder().namingPattern("export-pool-%d").daemon(true).build());

    /**
     * 异步导出Excel
     *
     * @param fileName       文件名称,原始名称即可,无需携带后缀
     * @param oriData        原始数据,目前支持的类型有DirectConnectDbOriData、ReportDataVO,新增数据类型时,增加对应的ConvertHandler
     * @param exportStrategy 导出策略
     * @return ExportResultVO 异步时,返回任务编号。同步时,直接返回文件地址
     * @see ITaskService#getStatus(long)  轮询任务编号获取导出状态
     */
    public <T> ExportResultVO doExport(String fileName, T oriData, ExportStrategy exportStrategy) throws Exception {
        if (!(oriData instanceof DirectConnectDbOriData) && !(oriData instanceof ReportDataVO)) {
            throw new BusinessException("原始数据类型目前不支持" + oriData.getClass().getSimpleName() + ",请使用DirectConnectDbOriData、ReportDataVO");
        }
        if (exportStrategy.getAsyncInfo().isAsync()) {
            Long taskId = taskService.createTask();
            exportStrategy.getAsyncInfo().setTaskId(taskId);
            threadPool.submit(() -> {
                try {
                    ExportContext context = new ExportContext(this, fileName, oriData, exportStrategy);
                    context.next();
                } catch (Exception e) {
                    ExportTask task = taskService.getStatus(taskId);
                    task.setTaskStatus(0);
                    if (e instanceof BusinessException) {
                        BusinessException bizException = (BusinessException) e;
                        task.setErrMsg(bizException.toString());
                    } else {
                        task.setErrMsg(e.getMessage());
                    }
                    taskService.updateTask(task);
                }
            });
            ExportResultVO resultVO = new ExportResultVO();
            resultVO.setTaskId(taskId);
            return resultVO;
        } else {
            ExportContext context = new ExportContext(this, fileName, oriData, exportStrategy);
            //可能会抛出异常,直接交给外层处理
            context.next();
            String downloadUrl = context.getDownloadUrl();
            ExportResultVO resultVO = new ExportResultVO();
            resultVO.setDownLoadUrl(downloadUrl);
            return resultVO;
        }
    }
}
